( function () {
/**
 * MDD is a special format that stores a position for every vertex in a model for every frame in an animation.
 * Similar to BVH, it can be used to transfer animation data between different 3D applications or engines.
 *
 * MDD stores its data in binary format (big endian) in the following way:
 *
 * number of frames (a single uint32)
 * number of vertices (a single uint32)
 * time values for each frame (sequence of float32)
 * vertex data for each frame (sequence of float32)
 */

class MDDLoader extends THREE.Loader {
  constructor(manager) {
    super(manager);
  }

  load(url, onLoad, onProgress, onError) {
    const scope = this;
    const loader = new THREE.FileLoader(this.manager);
    loader.setPath(this.path);
    loader.setResponseType('arraybuffer');
    loader.load(url, function (data) {
      onLoad(scope.parse(data));
    }, onProgress, onError);
  }

  parse(data) {
    const view = new DataView(data);
    const totalFrames = view.getUint32(0);
    const totalPoints = view.getUint32(4);
    let offset = 8; // animation clip

    const times = new Float32Array(totalFrames);
    const values = new Float32Array(totalFrames * totalFrames).fill(0);

    for (let i = 0; i < totalFrames; i++) {
      times[i] = view.getFloat32(offset);
      offset += 4;
      values[totalFrames * i + i] = 1;
    }

    const track = new THREE.NumberKeyframeTrack('.morphTargetInfluences', times, values);
    const clip = new THREE.AnimationClip('default', times[times.length - 1], [track]); // morph targets

    const morphTargets = [];

    for (let i = 0; i < totalFrames; i++) {
      const morphTarget = new Float32Array(totalPoints * 3);

      for (let j = 0; j < totalPoints; j++) {
        const stride = j * 3;
        morphTarget[stride + 0] = view.getFloat32(offset);
        offset += 4; // x

        morphTarget[stride + 1] = view.getFloat32(offset);
        offset += 4; // y

        morphTarget[stride + 2] = view.getFloat32(offset);
        offset += 4; // z
      }

      const attribute = new THREE.BufferAttribute(morphTarget, 3);
      attribute.name = 'morph_' + i;
      morphTargets.push(attribute);
    }

    return {
      morphTargets: morphTargets,
      clip: clip
    };
  }

}

THREE.MDDLoader = MDDLoader;
} )();
